﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Frogger
{
    class LevelOneManager : Microsoft.Xna.Framework.DrawableGameComponent
    {
        /*local class variables to hold sprites*/
        SpriteBatch __spriteBatch;  //Main component to draw sprites
        Frog __player;     //The frog that the user controls
        List<Automobile> __spriteList = new List<Automobile>(); //A list which contains all the enemies and bugs
	
	/**********************wei*********************************************/
        List<Lane> __laneList;
        Texture2D __laneTexture;
        bool __mousePressed = false;
        Point __mouseStart;
        Point __mouseEnd;
        int __chosenLane = -1;
        List<Entity> __mouseDragPointList;
        Texture2D __mouseDragPointTexture;
        List<Entity> __barList;
        /******************************************************88*************/
        /*list to read the information of cars and bugs from file*/
        private readonly SortedList __scriptsCache = new SortedList();

        int __nextSprite = 0;           //Pause between two notes, either from script or default
        double __lastSpawnTime = 0;     //Last time when the sprite spawns
        int __currentSpriteIndex = 0;   //Index of the scriptsCache
        int __spriteMinSpeed = 6;       //The minimum speed of the sprite
        int __spriteMaxSpeed = 10;      //The maximum speed of the sprite
        
        int __unitSize;                 /*Unit size of the sprites, which is 1/19 of the height of the screen, 
                                        **for the sake of the number of the total notes for this music
                                        */



        //temperary variables
        int enemySpawnMinMilliseconds = 1000;
        int enemySpawnMaxMilliseconds = 2000;
        int nextSpawnTimeChange = 5000;
        int timeSinceLastSpawnTimeChange = 0;
        int nextSpawnTime = 0;

        //Spawning possibilities
        int __likelihoodCar = 40;
        int __likelihoodAmbulance = 30;
        int __likelihoodLuas = 20;

        //Scores
        int __scoreCar = 10;
        int __scoreAmbulance = 20;
        int __scoreLuas = 40;
        int __scoreDoubleBuse = 50;

        //Timeout for frog state
        int __frogStateExpiration = 0;

        //Invoke the base constructor, the DrawableGameComponent()
        public LevelOneManager(Game game)
            : base(game)
        {
        }

        public override void Initialize()
        {
            base.Initialize();

            /*The unit size of each note is 1/19 of the total screen height*/
            this.__unitSize = Game.GraphicsDevice.PresentationParameters.BackBufferHeight / 19;
            /* 
            ** Load the script from file, which locates in 
            ** "..\Frogger Note\Frogger\Frogger\bin\x86\Debug\Script"
            **/
            LoadScriptt();
        }

        protected override void LoadContent()
        {
            /*Initialize the spriteVatch for futer use*/
            __spriteBatch = new SpriteBatch(Game.GraphicsDevice);

            /*Initialize the player*/
            this.__player = new Frog(
                Game.Content.Load<Texture2D>(@"Images/frog"),
                new Vector2(Game.Window.ClientBounds.Width / 2,
                  Game.Window.ClientBounds.Height / 2),
                new Point(292, 222), 10, new Point(0, 0),
                new Point(3, 1), Vector2.Zero, 0.5f,null);//last param is a cue_Iric

	    /*************************************************************************************************/
            __laneTexture = Game.Content.Load<Texture2D>(@"Images/road");
            for(int i=0;i<3;i++)
            {
                __laneList.Add(new Lane(__laneTexture, new Vector2(0,Game.Window.ClientBounds.Height/3 * i), 
                    new Point(800,150), 2, new Point(0,0), new Point(1,1), new Vector2(0,0),1,null,0));
            }
            for (j = 0; j < 4; j++)
            {
                Entity e = new Entity(Game.Content.Load<Texture2D>(@"stopBar"), Vector2.Zero, new Point(70, 300), 1, new Point(0, 0), new Point(1, 5), null, 0);
                e.setVisible(false);
                __barList.Add(e);
            }
            __mouseDragPointTexture = Game.Content.Load<Texture2D>(@"Images/points");

/*************************************************************************************************/

            base.LoadContent();
        }

        public override void Update(GameTime gameTime)
        {
            // Create objects occording to the script_Iric
            // Time to spawn enemy?
            nextSpawnTime -= gameTime.ElapsedGameTime.Milliseconds;
            if (nextSpawnTime < 0)
            {
                Spawn();

                // Reset spawn timer
                ResetSpawnTime();
            }

            // Update all the sprites in lists and the frog
            UpdateSprites(gameTime);

            AdjustSpawnTimes(gameTime);

            // Check whether the temporary state of the frog has expired
            CheckStateExpiration( gameTime );

            base.Update( gameTime );
        }

        protected void UpdateSprites(GameTime gameTime)
        {
            // Update player
            this.__player.Update(gameTime, Game.Window.ClientBounds);

            // Update all non-player sprites
            for (int i = 0; i < this.__spriteList.Count; ++i)
            {
                Automobile s = this.__spriteList[i];
                s.Update(gameTime, Game.Window.ClientBounds);

                // Check for collisions
                if (s.collisionRect.Intersects(this.__player.collisionRect))
                {

                    /*Codes remained to fill about how to handle collision detection__Iric*/
                    // Remove collided sprite from the game
                    this.__spriteList.RemoveAt(i);
                    --i;
                }

                // Remove object if it is out of bounds
                if (s.IsOutOfBounds(Game.Window.ClientBounds))
                {
                    this.__spriteList.RemoveAt(i);
                    --i;
                }
            }

	    /*************************************************************************************************************************/
            mouseEff = MouseEffect();
            if (mouseEff == 1 &&  __laneList[__chosenLane].__currentLaneState != Lane.LaneState.Stop)
            {
                __laneList[__chosenLane].__currentLaneState = Lane.LaneState.SpeedUp;
            }
            else if (MouseEffect == 2)
            {
                __laneList[__chosenLane].__currentLaneState = Lane.LaneState.Stop;

                __barList[__chosenLane].SetPosition(__laneList[__chosenLane].FirstCarPos());
                __barList[__chosenLane].SetVisible(true);
            }

           

            for (int i = 0; i < __laneList.Count; i++)
            {
                if (__barList[i].GetVisible() && __laneList[i].__stopCount > (Constants.CAR_STOP_TIME/5) * (__barList[i].GetCurrentFrame().X+1))
                {
                    __barList[i].SetCurrentFrame(__barList[i].GetCurrentFrame().X + 1, __barList[i].GetCurrentFrame().Y);
                    if (__barList[i].GetCurrentFrame().X == 4)
                        __barList[i].SetVisible(false);
                }

                if (__laneList[i].CheckOutOfScreen(Game.Window.ClientBounds))
                {
                    if (__laneList[i].CheckOutOfBound(Game.Window.ClientBounds))
                    {
                        __laneList.RemoveAt(i);
                        i--;
                    }
                    else
                    {
                        if (__laneList.Count <= 4)
                        {
                            __laneList.Add(new Lane(__laneTexture, new Vector2(0, -150),
                                    new Point(800, 150), 2, new Point(0, 0), new Point(1, 1), new Vector2(0, 0), 1, null, 0));

                            __laneList[i].Update(gameTime, Game.Window.ClientBounds);
                        }
                    }
                }
                else
                {
                    __laneList[i].Update(gameTime, Game.Window.ClientBounds);
                }
            }
/************************************************************************************************************************************/

            
        }

        public override void Draw(GameTime gameTime)
        {
            this.__spriteBatch.Begin(SpriteBlendMode.AlphaBlend,
                SpriteSortMode.FrontToBack, SaveStateMode.None);

            // Draw the player
            this.__player.Draw(gameTime, this.__spriteBatch);

            // Draw all sprites
            foreach (Entity s in this.__spriteList)
            {
                s.Draw(gameTime, this.__spriteBatch);
            }

	    /*************************************************************************************/
            foreach (Lane l in this.__laneList)
                l.Draw(gameTime, this.__spriteBatch);

            foreach (Entity m in this.__mouseDragPointList)
                m.Draw(gameTime, this.__spriteBatch);
/*************************************************************************************/

            this.__spriteBatch.End();

            base.Draw(gameTime);
        }

	/**************************************************************************************************/
        public int MouseEffect()
        {
            if (Mouse.GetState().LeftButton == ButtonState.Pressed)
            {
                if (__mousePressed == false)
                {
                    __mousePressed = true;
                    __mouseStart.X = Mouse.GetState().X;
                    __mouseStart.Y = Mouse.GetState().Y;

                    //Check which lane
                    for (int i = 0; i < __laneList.Count; i++)
                    {
                        Rectangle r = new Rectangle(__laneList[i].GetPosition().X,__laneList[i].GetPosition.Y,
                                                    __laneList[i].GetFrameSize().X,__laneList[i].GetFrameSize().Y);
                        if (r.Contains(__mouseStart))
                        {
                            __chosenLane = i;
                            //change the road color
                        }
                    }

                }

                //draw line
                __mouseDragPointList.Add(new Entity(__mouseDragPointTexture, new Vector2(Mouse.GetState().X, Mouse.GetState().Y),
                    new Point(0, 0), 1, new Point(0, 0), new Point(1, 1), 16, null, 0));
            }

            if (__mousePressed==true && Mouse.GetState().LeftButton == ButtonState.Released)
            {
                __mousePressed = false;
                __mouseEnd.X = Mouse.GetState().X;
                __mouseEnd.Y = Mouse.GetState().Y;

                int x = __mouseEnd.X - __mouseStart.X;
                if (x < 0)
                    x = -x;
                int y = __mouseEnd.Y - __mouseEnd.Y;
                if (y < 0)
                    y = -y;

                __mouseDragPointList.RemoveAll();

                if (x > y)
                    return 1;
                else
                {
                    return 2;
                }
            }
            return 0;
        }
/*******************************************************************************************************/

        private void Spawn()
        {
            //Speed, position for the sprite
            Vector2 speed = Vector2.Zero;
            Vector2 position = Vector2.Zero;
            /* Special variable for cars, because the original image is not in
             * the right direction
             */
            bool rotation = false;

            //Set the size of sprites occording to the nextSprite time
            float spriteSize = 1f;

            // Default frame size
            Point frameSize = new Point(400, 300);

            // Choose which side of the screen to place cars
            switch (((Game1)Game).rnd.Next(2))
            {
                case 0: // LEFT to RIGHT
                    {
                        rotation = true;
                        position = new Vector2(
                            -frameSize.X, (this.__position.Y));
                        speed = new Vector2(((Game1)Game).rnd.Next(
                            this.__spriteMinSpeed,
                            this.__spriteMaxSpeed), 0);
                        break;
                    }
                case 1: // RIGHT to LEFT
                    {
                        rotation = false;
                        position = new
                            Vector2(
                            Game.GraphicsDevice.PresentationParameters.BackBufferWidth,
                            (this.__position.Y));

                        speed = new Vector2(-((Game1)Game).rnd.Next(
                            this.__spriteMinSpeed, this.__spriteMaxSpeed), 0);
                        break;
                    }
            }

            // Get random number between 0 and 99
            int random = ((Game1)Game).rnd.Next(100);
            // Create an Automobile.
            // Get new random number to determine whether to
            // to create a Car, Ambulance, DoubleBus or a Luas

            if (random < this.__likelihoodCar)
            {
                //Create a car
                this.__carList.Add(
                    new Car(Game.Content.Load<Texture2D>(@"images\car"),
                        position, frameSize, 10, new Point(0, 0),
                        new Point(1, 1), speed, "car",
                        this.__scoreCar, rotation));

            }
            else if (random < this.__likelihoodCar +
            this.__likelihoodAmbulance)
            {
                //Create an Ambulance
                this.__carList.Add(
                    new Car(Game.Content.Load<Texture2D>(@"images\speedCar"),
                        position, frameSize, 10, new Point(0, 0),
                        new Point(1, 1), speed, "ambulance",
                        this.__scoreCar, rotation));
            }
            else if (random < this.__likelihoodCar +
            this.__likelihoodAmbulance + this.__likelihoodLuas)
            {
                //Create a Luas
                this.__carList.Add(
                    new Car(Game.Content.Load<Texture2D>(@"images\luas"),
                        position, frameSize, 10, new Point(0, 0),
                        new Point(1, 1), speed, "luas",
                        this.__scoreCar, rotation));
            }
            else
            {
                //Create a DoubleBus
                this.__carList.Add(
                    new Car(Game.Content.Load<Texture2D>(@"images\bus"),
                        position, frameSize, 10, new Point(0, 0),
                        new Point(1, 1), speed, "bus",
                        this.__scoreCar, rotation));
            }
        }//end Spawn()

        private void ResetSpawnTime()
        {
            nextSpawnTime = ((Game1)Game).rnd.Next(
            enemySpawnMinMilliseconds,
            enemySpawnMaxMilliseconds);
        }

        protected void AdjustSpawnTimes(GameTime gameTime)
        {
            // If the spawn max time is > 500 milliseconds
            // decrease the spawn time if it is time to do
            // so based on the spawn-timer variables
            if (enemySpawnMaxMilliseconds > 500)
            {
                timeSinceLastSpawnTimeChange += gameTime.ElapsedGameTime.Milliseconds;
                if (timeSinceLastSpawnTimeChange > nextSpawnTimeChange)
                {
                    timeSinceLastSpawnTimeChange -= nextSpawnTimeChange;
                    if (enemySpawnMaxMilliseconds > 1000)
                    {
                        enemySpawnMaxMilliseconds -= 100;
                        enemySpawnMinMilliseconds -= 100;
                    }
                    else
                    {
                        enemySpawnMaxMilliseconds -= 10;
                        enemySpawnMinMilliseconds -= 10;
                    }
                }
            }
        }

        public Vector2 GetPlayerPosition()
        {
            return this.__player.GetPosition;
        }


        protected void CheckStateExpiration(GameTime gameTime)
        {
            // Is a frog state active?
            if (__frogStateExpiration > 0)
            {
                // Decrement power-up timer
                __frogStateExpiration -=
                gameTime.ElapsedGameTime.Milliseconds;
                if (__frogStateExpiration <= 0)
                {
                    // If power-up timer has expired, end all power-ups
                    __frogStateExpiration = 0;
                    this.__player.__currentFrogState = Frog.FrogState.Stop;
                    __player.ResetScale();
                    __player.ResetSpeed();

                }
            }
        }

        //Method to load the script_Iric
        protected void LoadScriptt()
        {
            var fsIn = new FileStream( @"Script\in.dat", FileMode.Open,
                                        FileAccess.Read );
            TextReader reader = new StreamReader( fsIn );

            string line;
            int lineNum = 1;

            while ( ( line = reader.ReadLine() ) != null )
            {

                if ( lineNum > 1 ) //start from the second line   
                {

                    __scriptsCache.Add( lineNum, line );//add the line to the cache  

                }

                lineNum++;//increment the line number

            }

            reader.Close();

            fsIn.Close();  
        }
    }
}
